package xml;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import xml.elements.Uid;

//Eu coloquei isto:
import xml.elements.UidCall;


import xml.others.FacadeConstructorUID;
import xml.others.IdManager;
import xml.uidElements.AbstractState;
import xml.uidElements.InitialState;
import xml.uidElements.PosCondition;
import xml.uidElements.PreCondition;
import xml.uidElements.State;
import xml.uidElements.Text;
import xml.uidElements.TextualNote;
import xml.uidElements.Transition;
import xml.uidElements.TransitionFromSet;
import xml.uidElements.UID;
import xml.uidElements.UIDCall;
import xml.uidElements.entry.DataEntry;
import xml.uidElements.entry.DataItem;
import xml.uidElements.entry.DataItemSet;
import xml.uidElements.entry.EnumeratedData;
import xml.uidElements.entry.EnumeratedOption;
import xml.uidElements.entry.ORDataEntry;
import xml.uidElements.entry.OptionalDataItem;
import xml.uidElements.entry.Structure;
import xml.uidElements.entry.StructureSet;
import xml.uidElements.entry.SubState;
import xml.uidElements.entry.XORDataEntry;
import xml.uidElements.output.DataOutput;
import xml.uidElements.output.OutputDataItem;
import xml.uidElements.output.OutputDataItemSet;
import xml.uidElements.output.OutputStructure;
import xml.uidElements.output.OutputStructureSet;

import com.thoughtworks.xstream.XStream;

public class ConverterToUidXml {

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) {

		if (args.length == 1) {

			ConverterToUidXml converter = new ConverterToUidXml();
			converter.convert(args[0], args[0].replaceAll(".xuid", ".xmi"));

		} else if (args.length == 2) {

			ConverterToUidXml converter = new ConverterToUidXml();
			converter.convert(args[0], args[1]);

		} else {
			// por que nao aparece no Prompt ?!
			// System.out.println("Erro: Entre com o nome do arquivo.");
			// System.out
			// .println("Entre com os argumentos: \n conv.jar entrada.xuid saida.xml \n or \n conv.jar entrada.xuid");
			// System.exit(0);
			boolean exit = false;
			while(!exit){
			System.out.println("Entre com o nome do arquivo.");
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr);
			String str = "";
			try {
				str = "src/testFiles/" + br.readLine();
				if (!str.contains(".xuid")) {
					str += ".xuid";
				}
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
			ConverterToUidXml converter = new ConverterToUidXml();
			converter.convert(str, str.replaceAll(".xuid", ".xmi"));
			System.out.println("Done!");
			exit = true;
			}
		}

	}

	public ConverterToUidXml() {

	}

	public void convert (String fileInput, String fileOutput) {

		Uid d = deserialize(fileInput);

		d.setId(fileInput);

		new IdManager().setValidIdsAndFindIdInitialState(d);

		new FactoryUID("default", "default").insertElementsOfUid(d);

		serialize(fileOutput, FacadeConstructorUID.getInstance());
		
		FacadeConstructorUID.removeInstance();

	}

	@SuppressWarnings("unchecked")
	public Uid deserialize(String urlxml) {
		Uid d = null;
		try {
			JAXBContext context;
			context = JAXBContext.newInstance("xml.elements");
			Unmarshaller unmarshaller = context.createUnmarshaller();
			JAXBElement<Uid> element = (JAXBElement<Uid>) unmarshaller.unmarshal(new File(urlxml));
			d = element.getValue();
		} catch (JAXBException e) {
			System.out.println(e);
			e.printStackTrace();
		}
		return d;

	}

	public void serialize(String fileOutput, FacadeConstructorUID u) {

		XStream xstream = new XStream();

		defineAliases(xstream);

		try {
			PrintStream saida;
			saida = new PrintStream(new File(fileOutput));
			saida.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
			saida.println("<!DOCTYPE UID-set SYSTEM 'file:./uid-2-0.dtd'>");
			xstream.toXML(u, saida);
			saida.close();
		} catch (FileNotFoundException e) {
			System.out.println(e);
			e.printStackTrace();
		}

	}

	private void defineAliases(XStream xstream) {
		xstream.alias("UID-set", FacadeConstructorUID.class);
		xstream.alias("UID", UID.class);
		xstream.alias("state", State.class);
		xstream.alias("substate", SubState.class);
		xstream.alias("initial-state", InitialState.class);

		//Eu coloquei este:
//		xstream.alias("UID-call", UidCall.class);
		xstream.alias("UID-call", UIDCall.class);

		
		xstream.alias("pre-condition", PreCondition.class);
		xstream.alias("pos-condition", PosCondition.class);
		xstream.alias("textual-note", TextualNote.class);
		xstream.alias("transition", Transition.class);
		xstream.alias("transition-from-set", TransitionFromSet.class);

		xstream.alias("data-entry", DataEntry.class);
		xstream.alias("data-output", DataOutput.class);

		xstream.alias("data-item", DataItem.class);
		xstream.alias("data-item-set", DataItemSet.class);

		xstream.alias("optional-data-item", OptionalDataItem.class);

		xstream.alias("enumerated-data", EnumeratedData.class);
		xstream.alias("enumerated-option", EnumeratedOption.class);

		xstream.alias("OR-data-entry", ORDataEntry.class);
		xstream.alias("XOR-data-entry", XORDataEntry.class);

		xstream.alias("output-data-item", OutputDataItem.class);
		xstream.alias("output-data-item-set", OutputDataItemSet.class);

		xstream.alias("structure", Structure.class);
		xstream.alias("structure-set", StructureSet.class);

		xstream.alias("output-structure", OutputStructure.class);
		xstream.alias("output-structure-set", OutputStructureSet.class);

		xstream.alias("text", Text.class);

		xstream.aliasField("initial-state", UID.class, "initialState");
		xstream.aliasField("input-structure-elements", Structure.class,"listDataItemsAndStructures");
		xstream.aliasField("data-item", OptionalDataItem.class, "dataItem");
		xstream.aliasField("data-item", DataItemSet.class, "dataItem");
		xstream.aliasField("min-cardinality", DataItemSet.class,"cardinalityMin");
		xstream.aliasField("max-cardinality", DataItemSet.class,"cardinalityMax");
		xstream.aliasField("min-cardinality", StructureSet.class,"cardinalityMin");
		xstream.aliasField("max-cardinality", StructureSet.class,"cardinalityMax");
		xstream.aliasField("min-cardinality", OutputDataItemSet.class,"cardinalityMin");
		xstream.aliasField("max-cardinality", OutputDataItemSet.class,"cardinalityMax");
		xstream.aliasField("output-data-item", OutputDataItemSet.class,"dataItem");
		xstream.aliasField("min-cardinality", OutputStructureSet.class,"cardinalityMin");
		xstream.aliasField("max-cardinality", OutputStructureSet.class,"cardinalityMax");
		xstream.aliasField("min-selection", EnumeratedData.class,"cardinalityMin");
		xstream.aliasField("max-selection", EnumeratedData.class,"cardinalityMax");

		xstream.aliasField("data-item", ORDataEntry.class, "dataItem1");
		xstream.aliasField("data-item", ORDataEntry.class, "dataItem2");
		xstream.aliasField("data-item", XORDataEntry.class, "dataItem1");
		xstream.aliasField("data-item", XORDataEntry.class, "dataItem2");

		xstream.aliasField("output-structure-elements", OutputStructure.class,
				"listDataItemsAndStructures");
		xstream.aliasField("output-structure-elements",
				OutputStructureSet.class, "listDataItemsAndStructures");

		xstream.aliasField("target-type", Transition.class, "targetType");
		xstream.aliasField("no-return", Transition.class, "noReturn");

		xstream.useAttributeFor(UID.class, "id");
		xstream.useAttributeFor(UID.class, "name");
		xstream.useAttributeFor(AbstractState.class, "id");
		xstream.useAttributeFor(PosCondition.class, "condition");
		xstream.useAttributeFor(PreCondition.class, "condition");
		xstream.useAttributeFor(TextualNote.class, "note");

		//Eu coloquei estes dois:
		xstream.useAttributeFor(UIDCall.class, "id");
		xstream.useAttributeFor(UIDCall.class, "name");

		xstream.useAttributeFor(DataItem.class, "name");
		xstream.useAttributeFor(DataItem.class, "domain");
		xstream.useAttributeFor(DataItemSet.class, "cardinalityMin");
		xstream.useAttributeFor(DataItemSet.class, "cardinalityMax");
		xstream.useAttributeFor(DataItemSet.class, "optional");
		xstream.useAttributeFor(Structure.class, "name");
		xstream.useAttributeFor(Structure.class, "optional");
		xstream.useAttributeFor(StructureSet.class, "cardinalityMin");
		xstream.useAttributeFor(StructureSet.class, "cardinalityMax");
		xstream.useAttributeFor(StructureSet.class, "optional");

		xstream.useAttributeFor(OutputDataItem.class, "name");
		xstream.useAttributeFor(OutputDataItem.class, "domain");
		xstream.useAttributeFor(OutputDataItem.class, "optional");
		xstream.useAttributeFor(OutputDataItemSet.class, "cardinalityMin");
		xstream.useAttributeFor(OutputDataItemSet.class, "cardinalityMax");
		xstream.useAttributeFor(OutputDataItemSet.class, "optional");
		xstream.useAttributeFor(OutputStructure.class, "name");
		xstream.useAttributeFor(OutputStructure.class, "optional");
		xstream.useAttributeFor(OutputStructureSet.class, "name");
		xstream.useAttributeFor(OutputStructureSet.class, "optional");
		xstream.useAttributeFor(OutputStructureSet.class, "cardinalityMin");
		xstream.useAttributeFor(OutputStructureSet.class, "cardinalityMax");
		xstream.useAttributeFor(EnumeratedData.class, "name");
		xstream.useAttributeFor(EnumeratedData.class, "optional");
		xstream.useAttributeFor(EnumeratedData.class, "cardinalityMin");
		xstream.useAttributeFor(EnumeratedData.class, "cardinalityMax");
		xstream.useAttributeFor(EnumeratedOption.class, "name");
		xstream.useAttributeFor(Text.class, "id");
		xstream.useAttributeFor(Text.class, "data");
		xstream.useAttributeFor(Text.class, "optional");
		xstream.useAttributeFor(Transition.class, "id");
		xstream.useAttributeFor(Transition.class, "target");
		xstream.useAttributeFor(Transition.class, "targetType");
		xstream.useAttributeFor(Transition.class, "noReturn");
		xstream.useAttributeFor(Transition.class, "condition");
		xstream.useAttributeFor(Transition.class, "option");
		xstream.useAttributeFor(TransitionFromSet.class, "number");

		//Eu coloquei este:
		xstream.addImplicitCollection(UID.class, "setUIDCalls");

		xstream.addImplicitCollection(UID.class, "setStates");
		xstream.addImplicitCollection(UID.class, "listPreConditions");
		xstream.addImplicitCollection(UID.class, "listPosConditions");
		xstream.addImplicitCollection(UID.class, "listTextualNotes");
		// xstream.addImplicitCollection(UID.class, "setTransitions");
		xstream.addImplicitCollection(AbstractState.class, "listElements");
		xstream.addImplicitCollection(AbstractState.class,"listOutgoingTransitions");
		xstream.addImplicitCollection(DataEntry.class, "element");
		xstream.addImplicitCollection(DataOutput.class, "element");
		xstream.addImplicitCollection(OutputStructure.class,"listOutgoingTransitions");
		xstream.addImplicitCollection(OutputDataItem.class,"listOutgoingTransitions");
		xstream.addImplicitCollection(OutputStructureSet.class,"listOutgoingTransitions");
		xstream.addImplicitCollection(OutputDataItemSet.class,"listOutgoingTransitions");
		xstream.addImplicitCollection(Structure.class,"listDataItemsAndStructures");
		xstream.addImplicitCollection(EnumeratedData.class,"listEnumeratedOptions");
		xstream.addImplicitCollection(FacadeConstructorUID.class, "setUIDs");
	}
}